/**
  ******************************************************************************
  * @file    OLED_SSD1315_Config.c
  * @brief   I2C总线模拟驱动OLED（软件实现）
  * @author  18408107475@163.com
  * @version V1.0.0
  * @date    2025-6-15
  * @note    硬件特性：
  *          - 显示面板：128x64 有机LED
  *          - 控制器：Solomon Systech SSD1315
  *          - 接口：I2C（支持400kHz高速模式）
  *          - 显存组织：8页 x 128列 x 8位
  *          - 电源需求：3.3V供电，内置DC-DC升压
  *
  * @attention 移植说明：
  *          1. 硬件连接：
  *             - SCL：PB6（需4.7K上拉电阻）
  *             - SDA：PB7（需4.7K上拉电阻）
  *             - RESET：建议硬件复位电路（10uF电容+10K电阻）
  *          2. 软件适配：
  *             - 修改IIC_GPIO_Config()中的端口定义
  *             - 调整Delay_us()为实际平台的微秒延时
  *             - 字库需存储在程序存储器（Flash）
  *          3. 功耗说明：
  *             - 全亮模式：约20mA
  *             - 休眠模式：<10uA
  ******************************************************************************
  */

/* 包含头文件 ----------------------------------------------------------------*/
#include "OLED_SSD1315_Config.h"
#include "OLED_SSD1315_Font.h"

/*
	OLED的显存
	存放格式如下.
	[0]0 1 2 3 ... 127
	[1]0 1 2 3 ... 127
	[2]0 1 2 3 ... 127
	[3]0 1 2 3 ... 127
	[4]0 1 2 3 ... 127
	[5]0 1 2 3 ... 127
	[6]0 1 2 3 ... 127
	[7]0 1 2 3 ... 127
*/

/**
  * @brief  I2C起始信号
  * @note   产生I2C起始条件：SCL高电平时SDA下降沿
  */
void OLED_SSD1315_Start(void)
{
    IIC_Start();  // 调用底层I2C驱动
}

/**
  * @brief  I2C停止信号  
  * @note   产生I2C停止条件：SCL高电平时SDA上升沿
  */
void OLED_SSD1315_Stop(void)
{
    IIC_Stop();   // 调用底层I2C驱动
}

/**
  * @brief  等待从机应答
  * @retval true:收到ACK, false:无应答
  */
bool OLED_SSD1315_Wait_ACK(void)
{
    return IIC_Wait_Ack();  // 检测SDA电平
}

/**
  * @brief  写入命令到OLED
  * @param  IIC_command: 要写入的命令字节
  * @retval true:成功, false:失败
  */
bool OLED_SSD1315_Write_Command(u8 IIC_command)
{
    // 发送：地址 + 命令标识 + 命令字节
    return IIC_Write_Data(OLED_SSD1315_SLAVE_ADDRESS, WRITE_COMMAND, IIC_command);
}

/**
  * @brief  写入数据到OLED
  * @param  IIC_Data: 要写入的数据字节  
  * @retval true:成功, false:失败
  */
bool OLED_SSD1315_Write_Data(u8 IIC_Data)
{
	// 发送：地址 + 数据标识 + 数据字节
	return IIC_Write_Data(OLED_SSD1315_SLAVE_ADDRESS, WRITE_DATA, IIC_Data);
}

/**
  * @brief  通用数据/命令写入函数
  * @param  Input_Value: 要写入的值
  * @param  Mode_selet: 模式选择 OLED_CMD/OLED_DATA
  */
void OLED_SSD1315_Dat_or_Cmd(u8 Input_Value, u8 Mode_selet)
{
	(Mode_selet) ? OLED_SSD1315_Write_Data(Input_Value) : OLED_SSD1315_Write_Command(Input_Value);
}

/**
 * @brief 填充整个OLED屏幕（128x64）为指定模式
 * @param pattern 填充模式（0x00=全灭，0xFF=全亮，0x55=棋盘格等）
 * @return true:成功, false:失败（I2C通信错误）
 * @note   填充顺序：逐页→逐列
 */
bool OLED_SSD1315_FillScreen(u8 pattern) 
{
    u8 page, col;
    
    for (page = 0; page < 8; page++)  // 遍历所有页（8页）
    {
        /* 设置当前页地址（0xB0~0xB7） */
        if (!OLED_SSD1315_Write_Command(0xB0 | page)) {
            return false;  // 页地址设置失败
        }
        
        /* 设置列起始地址（0x00=低4位, 0x10=高4位） */
        if (!OLED_SSD1315_Write_Command(0x00) || 
            !OLED_SSD1315_Write_Command(0x10)) {
            return false;  // 列地址设置失败
        }
        
        /* 填充当前页的128列 */
        for (col = 0; col < 128; col++) 
        {
            if (!OLED_SSD1315_Write_Data(pattern)) {
                return false;  // 数据写入失败
            }
        }
    }
    return true;  // 填充成功
}

/**
  * @brief  设置显示起始位置（精确定位像素位置）
  * @param  x: 列地址 (0-127)
  *         - 对应屏幕水平方向，每个单位代表1个像素列
  * @param  y: 页地址 (0-7)
  *         - 对应屏幕垂直方向，每页包含8行像素
  *         - 实际像素行号 = y*8 + bit_position
  * @note   SSD1315寻址机制：
  *         1. 采用分页式寻址架构，将64行分为8页（Page0-Page7）
  *         2. 列地址分为高4位（0x10-0x1F）和低4位（0x00-0x0F）
  *         3. 写入数据后列地址自动递增，页地址不变
  *         4. 坐标转换公式：
  *            - 页地址寄存器 = 0xB0 | (y & 0x07)
  *            - 列地址高4位  = ((x & 0xF0) >> 4) | 0x10
  *            - 列地址低4位  = x & 0x0F
  */
void OLED_SSD1315_Set_Pos(u8 x, u8 y)
{ 	
	/* 设置页地址（0xB0~0xB7） （Y轴） */
    OLED_SSD1315_Dat_or_Cmd(0xB0 | (y & 0x07), OLED_CMD);
	/* 设置列地址高4位（0x10~0x1F）*/
    OLED_SSD1315_Dat_or_Cmd(((x & 0xF0) >> 4) | 0x10, OLED_CMD);
	/* 设置列地址低4位（0x00~0x0F）*/
    OLED_SSD1315_Dat_or_Cmd((x & 0x0F), OLED_CMD);
}

/**
  * @brief  开启显示并配置电荷泵
  * @note   电源管理流程：
  *         1. 0x8D命令进入电荷泵配置模式
  *         2. 0x14启用内部DC-DC转换器（7.5V升压）
  *         3. 0xAF开启显示输出
  *         硬件特性：
  *         - 电荷泵为电容式倍压电路，需10uF以上滤波电容
  *         - 启动时间约500us，建议延时1ms后再操作显示
  */
void OLED_SSD1315_Display_On(void)
{
    OLED_SSD1315_Dat_or_Cmd(0x8D, OLED_CMD);  // 进入电荷泵配置模式
    OLED_SSD1315_Dat_or_Cmd(0x14, OLED_CMD);  // 启用内部升压（0x10为关闭）
	Delay_ms(1);                      		  // 等待电压稳定
    OLED_SSD1315_Dat_or_Cmd(0xAF, OLED_CMD);  // 开启显示（退出休眠模式）
}

/**
  * @brief  关闭显示并进入低功耗模式
  * @note   电源关闭流程：
  *         1. 0xAE关闭显示输出（停止扫描）
  *         2. 0x8D进入电荷泵配置
  *         3. 0x10关闭升压电路
  *         功耗参数：
  *         - 仅关闭显示时功耗约0.1mA（保持显存内容）
  *         - 关闭电荷泵后功耗<10uA（显存数据丢失）
  */
void OLED_SSD1315_Display_Off(void)
{
	OLED_SSD1315_Dat_or_Cmd(0xAE, OLED_CMD);  // 关闭显示输出
    OLED_SSD1315_Dat_or_Cmd(0x8D, OLED_CMD);  // 进入电荷泵配置
    OLED_SSD1315_Dat_or_Cmd(0x10, OLED_CMD);  // 关闭升压电路
}

/**
  * @brief  清屏函数（快速填充全0）
  * @note   实现原理：
  *         - 向所有显存写入0x00
  *         - 等效于memset(OLED_RAM, 0, 1024)
  *         性能参数：
  *         - 需传输1024字节，I2C@400kHz耗时约25ms
  *         - 比单独控制每个像素快100倍以上
  */
void OLED_SSD1315_Clear(void)
{
	OLED_SSD1315_FillScreen(0x00);	// 全屏写0
}

/**
  * @brief  全屏点亮（测试屏幕用）
  * @note   实现特点：
  *         - 向所有显存写入0xFF
  *         - 可用于检测坏点/亮度均匀性
  *         电流消耗：
  *         - 全亮时工作电流约20-30mA
  *         - 持续全亮可能影响OLED寿命
  */
void OLED_SSD1315_ON(void)
{
	OLED_SSD1315_FillScreen(0xFF);	// 全屏写1
}

/**
 * @brief 在指定位置显示一个字符
 * @param x 列起始位置 (0~127)
 * @param y 行起始位置 (单位: 页, 0~7)
 * @param chr 要显示的字符 (ASCII码)
 * @param char_size 字体大小 (8或16)
 * @note 使用前需确保已初始化字库(F6x8/F8X16)
 */
void OLED_SSD1315_ShowChar(u8 x, u8 y, u8 chr, u8 char_size) 
{
    // 参数校验
    if (x >= OLED_WIDTH) x = 0;
    if (y >= OLED_PAGE_NUM) y = 0;
    if (char_size != 8 && char_size != 16) char_size = 8;

    // 计算字库偏移量 (ASCII码转字库索引)
    u8 char_offset = chr - ' ';
    
    // 16x16字体处理
    if (char_size == 16) {
        // 显示上半部分 (8x16像素)
        OLED_SSD1315_Set_Pos(x, y);
        for (u8 i = 0; i < 8; i++) {
            OLED_SSD1315_Write_Data(F8X16[char_offset * 16 + i]);
        }
        
        // 显示下半部分 (8x16像素)
        OLED_SSD1315_Set_Pos(x, y + 1);
        for (u8 i = 0; i < 8; i++) {
            OLED_SSD1315_Write_Data(F8X16[char_offset * 16 + i + 8]);
        }
    } 
    // 8x8字体处理
    else {
        OLED_SSD1315_Set_Pos(x, y);
        for (u8 i = 0; i < 6; i++) {  // 6x8字体实际宽度为6像素
            OLED_SSD1315_Write_Data(F6x8[char_offset][i]);
        }
    }
}

/**
 * @brief 显示字符串
 * @param x,y 起始坐标
 * @param str 字符串指针
 * @param size 字体大小 (8/16)
 * @param wrap 是否自动换行 (true/false)
 */
void OLED_SSD1315_ShowString(u8 x, u8 y, const char* str, u8 size, bool wrap) {
    while (*str) {
        if (x >= OLED_WIDTH) {
            if (wrap) {
                x = 0;
                y += (size == 16) ? 2 : 1;
            } else break;
        }
        OLED_SSD1315_ShowChar(x, y, *str++, size);
        x += (size == 16) ? 8 : 6;  // 根据字体宽度调整步进
    }
}

/**
  * @brief  显示定长数字（自动补零）
  * @param  x: 起始列坐标 (0~127)
  * @param  y: 起始页坐标 (0~7)
  * @param  num: 要显示的数字（支持32位无符号整数）
  * @param  len: 显示位数（自动补零）
  *             - 示例：len=4,num=12 → "0012"
  *             - 实际位数不足时显示完整数字
  * @param  size: 字体大小 (8或16)
  * @note   特性：
  *         - 支持uint32_t最大值4294967295（10位）
  *         - 自动处理越界数字（超过指定位数时完整显示）
  *         - 固定宽度右对齐，不足位补零
  *         性能：
  *         - 使用静态缓冲区避免动态内存分配
  *         - 调用snprintf保证缓冲区安全
  */
void OLED_SSD1315_ShowNum(u8 x, u8 y, uint32_t num, u8 len, u8 size) 
{
    char str[16]; // 缓冲区（可容纳uint32_t最大值+结束符）
    
    /* 格式化数字：
       %0*d 表示：
       - * 由参数len指定宽度
       - 0 不足位补零
       - d 十进制格式 */
    snprintf(str, sizeof(str), "%0*d", len, num);
    
    /* 调用字符串显示函数
       - 关闭自动换行（false）
       - 使用指定字体大小 */
    OLED_SSD1315_ShowString(x, y, str, size, false);
}

/**
 * @brief 快速计算整数幂
 * @param base 基数
 * @param exponent 指数
 * @return 计算结果
 */
uint32_t OLED_Math_Pow(u8 base, u8 exponent)
{
    uint32_t result = 1;
    while (exponent--) {
        result *= base;
        // 加入溢出保护
        if (result > UINT32_MAX / base) {
            return UINT32_MAX;
        }
    }
    return result;
}

/**
 * @brief 显示16x16中文字符
 * @param x 列起始位置 (0~127)
 * @param y 行起始位置 (页地址，0~7)
 * @param index 汉字在字库中的索引
 * @note 使用前需确保已初始化Hzk字库
 */
void OLED_SSD1315_ShowChinese(u8 x, u8 y, u8 index)
{
    // 参数边界检查
    x = (x >= OLED_WIDTH - 15) ? OLED_WIDTH - 16 : x;
    y = (y >= OLED_PAGE_NUM - 1) ? OLED_PAGE_NUM - 2 : y;

    // 显示汉字上半部分
    OLED_SSD1315_Set_Pos(x, y);
    for (u8 t = 0; t < 16; t++) {
        OLED_SSD1315_Write_Data(Hzk[2 * index][t]);
    }

    // 显示汉字下半部分
    OLED_SSD1315_Set_Pos(x, y + 1);
    for (u8 t = 0; t < 16; t++) {
        OLED_SSD1315_Write_Data(Hzk[2 * index + 1][t]);
    }
}

/**
 * @brief 显示位图数据
 * @param x0 起始列 (0~127)
 * @param y0 起始页 (0~7)
 * @param x1 结束列 (x0+1~128)
 * @param y1 结束行 (y0+1~64)
 * @param bmp 位图数据数组
 * @note 位图数据需按页组织，每页数据连续存放
 */
bool OLED_SSD1315_DrawBitmap(u8 x0, u8 y0, u8 x1, u8 y1, const u8 *bmp)
{
    // 参数校验
    if (x0 >= OLED_WIDTH || y0 >= OLED_PAGE_NUM || 
        x1 > OLED_WIDTH || y1 > OLED_HEIGHT ||
        x0 >= x1 || y0 >= y1) {
        return false;
    }

    uint16_t index = 0;
    u8 page_start = y0;
    u8 page_end = (y1 + 7) / 8; // 计算结束页

    // 按页写入数据
    for (u8 page = page_start; page < page_end; page++) {
        OLED_SSD1315_Set_Pos(x0, page);
        
        // 写入当前页的所有列数据
        for (u8 col = x0; col < x1; col++) {
            if (!OLED_SSD1315_Write_Data(bmp[index++])) {
                return false;
            }
        }
    }
    return true;
}

void OLED_SSD1315_Init(void)
{
	IIC_Init();

	Delay_ms(200);

	OLED_SSD1315_Dat_or_Cmd(0xAE, OLED_CMD);	// 关闭显示（Display OFF）
	OLED_SSD1315_Dat_or_Cmd(0x00, OLED_CMD);	// 设置列地址低位（低4位）
	OLED_SSD1315_Dat_or_Cmd(0x10, OLED_CMD);	// 设置列地址高位（高4位）
	OLED_SSD1315_Dat_or_Cmd(0x40, OLED_CMD);	// 设置起始行地址（从第0行开始）
	OLED_SSD1315_Dat_or_Cmd(0xB0, OLED_CMD);	// 设置页地址（页0）
	OLED_SSD1315_Dat_or_Cmd(0x81, OLED_CMD);	// 对比度控制（设置对比度）
	OLED_SSD1315_Dat_or_Cmd(0xFF, OLED_CMD);	// 对比度值（最大亮度255）
	OLED_SSD1315_Dat_or_Cmd(0xA1, OLED_CMD);	// 设置段重映射（水平翻转）
	OLED_SSD1315_Dat_or_Cmd(0xA6, OLED_CMD);	// 正常显示（非反色）
	OLED_SSD1315_Dat_or_Cmd(0xA8, OLED_CMD);	// 设置多路复用比率（1~64）
	OLED_SSD1315_Dat_or_Cmd(0x3F, OLED_CMD);	// 占空比1/32（适用于64行屏幕）
	OLED_SSD1315_Dat_or_Cmd(0xC8, OLED_CMD);	// COM扫描方向（从下到上）
	OLED_SSD1315_Dat_or_Cmd(0xD3, OLED_CMD);	// 设置显示偏移（垂直偏移）
	OLED_SSD1315_Dat_or_Cmd(0x00, OLED_CMD);	// 偏移量=0（不偏移）

	OLED_SSD1315_Dat_or_Cmd(0xD5, OLED_CMD);	// 设置时钟分频（振荡器频率）
	OLED_SSD1315_Dat_or_Cmd(0x80, OLED_CMD);	// 默认分频比

	OLED_SSD1315_Dat_or_Cmd(0xD8, OLED_CMD);	// 关闭区域颜色模式（单色屏）
	OLED_SSD1315_Dat_or_Cmd(0x05, OLED_CMD);	// 默认值

	OLED_SSD1315_Dat_or_Cmd(0xD9, OLED_CMD);	// 设置预充电周期
	OLED_SSD1315_Dat_or_Cmd(0xF1, OLED_CMD);	// 预充电时间=15时钟周期

	OLED_SSD1315_Dat_or_Cmd(0xDA, OLED_CMD);	// 设置COM引脚硬件配置
	OLED_SSD1315_Dat_or_Cmd(0x12, OLED_CMD);	// 适用于64行屏幕

	OLED_SSD1315_Dat_or_Cmd(0xDB, OLED_CMD);	// 设置VCOMH电压（调节亮度）
	OLED_SSD1315_Dat_or_Cmd(0x30, OLED_CMD);	// 约0.83×VCC

	OLED_SSD1315_Dat_or_Cmd(0x8D, OLED_CMD);	// 启用电荷泵（内部升压）
	OLED_SSD1315_Dat_or_Cmd(0x14, OLED_CMD);	// 使能电荷泵

	OLED_SSD1315_Dat_or_Cmd(0xAF, OLED_CMD);	// 打开显示（Display ON）

}

